#include "IICMaster.h"
#include "my_timer.h"
#include "usb_com.h"
struct
{
    unsigned short delay;
    unsigned short delaytick;
    unsigned char *data;
    unsigned char state: 4;
    unsigned char error: 4;
    unsigned char len;
    unsigned char index;
} iicMaster;

/* 定义IIC总线连接的GPIO端口, 用户只需要修改下面4行代码即可任意改变SCL和SDA的引脚 */
#define IIC_SCL_GPIO GPIOB          /* 连接到SCL时钟线的GPIO */
#define IIC_SCL_PIN GPIO_PIN_10     /* 连接到SCL时钟线的GPIO */

#define IIC_SDA_GPIO GPIOB          /* 连接到SDA数据线的GPIO */
#define IIC_SDA_PIN GPIO_PIN_11     /* 连接到SDA数据线的GPIO */

#define BSP_SET_GPIO_1(gpio, pin)   gpio->BSRR = pin
#define BSP_SET_GPIO_0(gpio, pin)   gpio->BSRR = (uint32_t)(pin) << 16U

/* 定义读写SCL和SDA的宏 */
#define IIC_SCL_1() BSP_SET_GPIO_1(IIC_SCL_GPIO, IIC_SCL_PIN)   /* SCL = 1 */
#define IIC_SCL_0() BSP_SET_GPIO_0(IIC_SCL_GPIO, IIC_SCL_PIN)   /* SCL = 0 */

#define IIC_SDA_1() BSP_SET_GPIO_1(IIC_SDA_GPIO, IIC_SDA_PIN)   /* SDA = 1 */
#define IIC_SDA_0() BSP_SET_GPIO_0(IIC_SDA_GPIO, IIC_SDA_PIN)   /* SDA = 0 */

#define IIC_SDA_READ() ((IIC_SDA_GPIO->IDR & IIC_SDA_PIN) != 0) /* 读SDA口线状态 */
#define IIC_SCL_READ() ((IIC_SCL_GPIO->IDR & IIC_SCL_PIN) != 0) /* 读SCL口线状态 */

#define IIC_SDA_IN() //{GPIOB->CRH &= 0xffff0fff;GPIOB->CRH|=8<<12;}
#define IIC_SDA_OUT() //{GPIOB->CRH &= 0xffff0fff;GPIOB->CRH|=3<<12;}

/*
*********************************************************************************************************
*    函 数 名: bsp_InitIIC
*    功能说明: 配置IIC总线的GPIO，采用模拟IO的方式实现
*    形    参:  无
*    返 回 值: 无
*********************************************************************************************************
*/
void iic_init(void)
{
    static struct my_timer iicMasterTimer;
    GPIO_InitTypeDef gpio_init;
    gpio_init.Mode = GPIO_MODE_OUTPUT_PP;        /* 设置开漏输出 */
    gpio_init.Pull = GPIO_PULLUP;           /* 上下拉电阻不使能 */
    gpio_init.Speed = GPIO_SPEED_FREQ_HIGH; /* GPIO速度等级 */

    gpio_init.Pin = IIC_SCL_PIN;
    HAL_GPIO_Init(IIC_SCL_GPIO, &gpio_init);

    gpio_init.Mode = GPIO_MODE_OUTPUT_OD;        /* 设置开漏输出 */
    gpio_init.Pull = GPIO_NOPULL;                        /* 上下拉电阻不使能 */
    gpio_init.Speed = GPIO_SPEED_FREQ_HIGH; /* GPIO速度等级 */

    gpio_init.Pin = IIC_SDA_PIN;
    HAL_GPIO_Init(IIC_SDA_GPIO, &gpio_init);
    /* 给一个停止信号, 复位IIC总线上的所有设备到待机模式 */
    iic_stop();

    timer_register_set(&iicMasterTimer, 1, 0, 0, iic_master_loop, 0, 1);
}

/*
*********************************************************************************************************
*    函 数 名: iic_delay
*    功能说明: IIC总线位延迟，最快400KHz
*    形    参:  无
*    返 回 值: 无
*********************************************************************************************************
*/

static void iic_delay(void)
{
    //7us
    // long tick = 72;
    // while (tick--)
    // {
    //     __NOP();
    // };
    delay_us(7);
}

/*
*********************************************************************************************************
*    函 数 名: iic_Start
*    功能说明: CPU发起IIC总线启动信号
*    形    参:  无
*    返 回 值: 无
*********************************************************************************************************
*/
void iic_start(void)
{
    /* 当SCL高电平时，SDA出现一个下跳沿表示IIC总线启动信号 */
    IIC_SDA_OUT();
    IIC_SDA_1();
    IIC_SCL_1();
    iic_delay();
    IIC_SDA_0();
    iic_delay();

    IIC_SCL_0();
    iic_delay();
}

/*
*********************************************************************************************************
*    函 数 名: iic_Start
*    功能说明: CPU发起IIC总线停止信号
*    形    参:  无
*    返 回 值: 无
*********************************************************************************************************
*/
void iic_stop(void)
{
    /* 当SCL高电平时，SDA出现一个上跳沿表示IIC总线停止信号 */
    IIC_SDA_OUT();
    IIC_SDA_0();
    IIC_SCL_1();
    iic_delay();
    IIC_SDA_1();
    iic_delay();
}

/*
*********************************************************************************************************
*    函 数 名: iic_SendByte
*    功能说明: CPU向IIC总线设备发送8bit数据
*    形    参:  _ucByte ： 等待发送的字节
*    返 回 值: 无
*********************************************************************************************************
*/
void iic_sendbyte(unsigned char _ucByte)
{
    unsigned char i;
    IIC_SDA_OUT();
    /* 先发送字节的高位bit7 */
    for (i = 0; i < 8; i++)
    {
        if (_ucByte & 0x80)
        {
            IIC_SDA_1();
        }
        else
        {
            IIC_SDA_0();
        }
        iic_delay();
        IIC_SCL_1();
        iic_delay();
        IIC_SCL_0();
        if (i == 7)
        {
            IIC_SDA_1(); // 释放总线
        }
        _ucByte <<= 1; /* 左移一个bit */
        iic_delay();
    }
}

/*
*********************************************************************************************************
*    函 数 名: iic_ReadByte
*    功能说明: CPU从IIC总线设备读取8bit数据
*    形    参:  无
*    返 回 值: 读到的数据
*********************************************************************************************************
*/
unsigned char iic_readbyte(void)
{
    unsigned char i;
    unsigned char value;
    IIC_SDA_IN();
    /* 读到第1个bit为数据的bit7 */
    value = 0;
    for (i = 0; i < 8; i++)
    {
        value <<= 1;
        IIC_SCL_1();
        iic_delay();
        if (IIC_SDA_READ())
        {
            value++;
        }
        IIC_SCL_0();
        iic_delay();
    }
    return value;
}

/*
*********************************************************************************************************
*    函 数 名: iic_WaitAck
*    功能说明: CPU产生一个时钟，并读取器件的ACK应答信号
*    形    参:  无
*    返 回 值: 返回0表示正确应答，1表示无器件响应
*********************************************************************************************************
*/
unsigned char iic_waitack(void)
{
    unsigned long re, j = 0;
    IIC_SDA_IN();
    IIC_SDA_1(); /* CPU释放SDA总线 */
    iic_delay();
    IIC_SCL_1(); /* CPU驱动SCL = 1, 此时器件会返回ACK应答 */
    iic_delay();
    while (j++ < 7200000)
    {
        if (IIC_SDA_READ()) /* CPU读取SDA口线状态 */
        {
            re = 1;
        }
        else
        {
            re = 0;
            break;
        }
    }

    IIC_SCL_0();
    iic_delay();
    return re;
}
void iic_waitack1(void)
{
    IIC_SDA_IN();
    IIC_SDA_1(); /* CPU释放SDA总线 */
    iic_delay();
    IIC_SCL_1(); /* CPU驱动SCL = 1, 此时器件会返回ACK应答 */
    iic_delay();
}

void iic_waitack2(void)
{
    IIC_SCL_0();
    iic_delay();
}


/*
*********************************************************************************************************
*    函 数 名: iic_Ack
*    功能说明: CPU产生一个ACK信号
*    形    参:  无
*    返 回 值: 无
*********************************************************************************************************
*/
void iic_ack(void)
{
    IIC_SDA_OUT();
    IIC_SDA_0(); /* CPU驱动SDA = 0 */
    iic_delay();
    IIC_SCL_1(); /* CPU产生1个时钟 */
    iic_delay();
    IIC_SCL_0();
    iic_delay();
    IIC_SDA_1(); /* CPU释放SDA总线 */
}

/*
*********************************************************************************************************
*    函 数 名: iic_NAck
*    功能说明: CPU产生1个NACK信号
*    形    参:  无
*    返 回 值: 无
*********************************************************************************************************
*/
void iic_nack(void)
{
    IIC_SDA_OUT();
    IIC_SDA_1(); /* CPU驱动SDA = 1 */
    iic_delay();
    IIC_SCL_1(); /* CPU产生1个时钟 */
    iic_delay();
    IIC_SCL_0();
    iic_delay();
}

/*
*********************************************************************************************************
*    函 数 名: iic_CheckDevice
*    功能说明: 检测IIC总线设备，CPU向发送设备地址，然后读取设备应答来判断该设备是否存在
*    形    参:  _Address：设备的IIC总线地址
*    返 回 值: 返回值 0 表示正确， 返回1表示未探测到
*********************************************************************************************************
*/
unsigned char iic_checkdevice(unsigned char _Address)
{
    unsigned char ucAck;
    if (IIC_SDA_READ() && IIC_SCL_READ())
    {
        iic_start(); /* 发送启动信号 */

        /* 发送设备地址+读写控制bit（0 = w， 1 = r) bit7 先传 */
        iic_sendbyte(_Address | IIC_WR);
        ucAck = iic_waitack(); /* 检测设备的ACK应答 */

        iic_stop(); /* 发送停止信号 */

        return ucAck;
    }
    return 1; /* IIC总线异常 */
}
__weak void iic_callback(unsigned char state)
{

}
/// @brief 写入等待
void iic_write_wait()
{
    if (IIC_SDA_READ() == 0)
    {
        iic_waitack2();
        iicMaster.state = IIC_STATE_TX_BYTE_WAIT;
        if (iicMaster.index < iicMaster.len)
        {
            iic_sendbyte(iicMaster.data[iicMaster.index]);
            iicMaster.index++;
            iic_waitack1();
            iic_write_wait();
        }
        else
        {
            iic_stop();
            iicMaster.state = IIC_STATE_NONE;
            iic_callback(IIC_WRITE_OK);
            return;
        }
    }
}

/// @brief 读取等待
void iic_read_wait()
{
    if (IIC_SDA_READ() == 0)
    {
        iic_waitack2();
        for (iicMaster.index = 0; iicMaster.index < iicMaster.len; iicMaster.index++)
        {
            iicMaster.data[iicMaster.index] = iic_readbyte();
            if (iicMaster.index != iicMaster.len - 1)
            {
                iic_ack();
            }
            else
            {
                iic_nack();
            }
        }
        iic_stop();
        iic_callback(IIC_READ_OK);
        iicMaster.state = IIC_STATE_NONE;
    }
}

/// @brief 发起新请求
void iic_new()
{
    if (iicMaster.state != IIC_STATE_NONE)
    {
        iic_stop();
        if (iicMaster.state <= IIC_STATE_TX_BYTE_WAIT)
        {
            iic_callback(IIC_WRITE_FAIL);
        }
        else
        {
            iic_callback(IIC_READ_FAIL);
        }
    }
}

/// @brief iic数据读取
/// @param addr 读取地址
/// @param data 数据地址
/// @param len 数据长度
/// @param timeout 超时时间
void iic_read_bytes(unsigned char addr, unsigned char *data, unsigned char len, unsigned short timeout)
{
    iic_new();
    iic_start();
    iic_sendbyte(addr);
    iic_waitack1();

    iicMaster.data = data;
    iicMaster.index = 0;
    iicMaster.len = len;
    iicMaster.state = IIC_STATE_RX_ADDR_WAIT;
    iicMaster.error = 0;
    iicMaster.delay = timeout;
    iicMaster.delaytick = 0;
    iic_read_wait();
}

/// @brief 写入iic数据
/// @param addr 读取地址
/// @param data 数据地址
/// @param len 长度
/// @param timeout 超时时间
void iic_write_bytes(unsigned char addr, unsigned char *data, unsigned char len, unsigned short timeout)
{
    iic_new();
    iic_start();
    iic_sendbyte(addr);
    iic_waitack1();

    iicMaster.data = data;
    iicMaster.index = 0;
    iicMaster.len = len;
    iicMaster.state = IIC_STATE_TX_ADDR_WAIT;
    iicMaster.error = 0;
    iicMaster.delay = timeout;
    iicMaster.delaytick = 0;
    iic_write_wait();
}

/// @brief 1ms调用一次
void iic_master_loop(void*p)
{
    switch (iicMaster.state)
    {
        case IIC_STATE_TX_ADDR_WAIT:
        case IIC_STATE_TX_BYTE_WAIT:
            iicMaster.delaytick++;
            if (iicMaster.delaytick >= iicMaster.delay)
            {
                iic_stop();
                iic_callback(IIC_WRITE_FAIL);
                iicMaster.state = IIC_STATE_NONE;
            }
            else
            {
                iic_write_wait();
            }

            break;
        case IIC_STATE_RX_ADDR_WAIT:
            iicMaster.delaytick++;
            if (iicMaster.delaytick >= iicMaster.delay)
            {
                iic_callback(IIC_READ_FAIL);
                iicMaster.state = IIC_STATE_NONE;
                iic_stop();
            }
            else
            {
                iic_read_wait();
            }

            break;
        default:
            break;
    }

}
